#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ff.h"

#include "DevicesTime.h"
#include "DevicesFlash.h"

#include "rtthread.h"
#include "cpuusage.h"

#include "NesCPU6502.h"
#include "NesPPU.h"
#include "NesMapper.h"
#include "NesSystem.h"


FIL g_fileNESHandle = {0};
static uint8_t st_ucFileReadBuff[2048] = {0};

extern uint8_t nesRom[];
NesInfoType g_typeNesInfo = {0};


int64_t lNesTimeGet(void)
{
    return lTimeGetStamp();
}

void vNesDelayMs(int32_t iTime)
{
    rt_thread_mdelay(iTime);
}

void vNesMirrorTypeSet(int8_t cMirrorType)
{
    g_typeNesInfo.mirrorType = cMirrorType;
}

int8_t cNesFlashLoad(char *pucFileName)
{
    /* FatFs文件系统对象 */
    extern FATFS g_FATMountHanle;
    FRESULT res;
    int32_t iFileTotalLength = 0, iFileReadNow = 0;
    uint32_t uiFlashAddrNow = FLASH_NES_ROM_ADDR, uiFileReadLength = 0;

    printf("\n\rcNesLoadToFlash: %s\r\n", pucFileName);


    /* 判断磁盘是否挂载成功 */
    if(g_FATMountHanle.fs_type == 0)
    {
        printf("SD mount error.\r\n");
        return 1;
    }

    res = f_open(&g_fileNESHandle, pucFileName, FA_READ);
    if(res != FR_OK)
    {
        printf("f_open error: %d\r\n", res);
        return 2;
    }

    iFileTotalLength = f_size(&g_fileNESHandle);
    printf("NES file size: %d\r\n", iFileTotalLength);
    if(iFileTotalLength <= 0)
    {
        f_close(&g_fileNESHandle);
        return 3;
    }


    printf("NES file load start.\r\n");
    while(iFileTotalLength > 0)
    {
        iFileReadNow = iFileTotalLength > 2048 ? 2048 : iFileTotalLength;

        f_read(&g_fileNESHandle, st_ucFileReadBuff, iFileReadNow, &uiFileReadLength);

        cFlashWriteDatas(uiFlashAddrNow, st_ucFileReadBuff, iFileReadNow);

        uiFlashAddrNow += iFileReadNow;
        iFileTotalLength -= iFileReadNow;
    }
    printf("NES file load succeed.\r\n");

    f_close(&g_fileNESHandle);


    g_typeNesInfo.NES_ROM = (uint8_t *)FLASH_NES_ROM_ADDR;


    return 0;
}

int8_t cNesLoad(void)
{
    if(g_typeNesInfo.NES_ROM == NULL)
        return -1;

    /* 从封装好的Flash读取函数读取数据，会暂时关闭mcu icache，以真实的读取到文件头数据 */
    memcpy(g_typeNesInfo.headers, g_typeNesInfo.NES_ROM, 16);

    /* NES头部信息解析 */
    g_typeNesInfo.PRG_ROMSize    =  g_typeNesInfo.headers[4] * 1024 * 16;
    g_typeNesInfo.CHR_ROMSize    =  g_typeNesInfo.headers[5] * 1024 * 8;
    g_typeNesInfo.PRG_RAMSize    =  g_typeNesInfo.headers[8] * 1024 * 8;

    g_typeNesInfo.mapperType     =  g_typeNesInfo.headers[6] >> 4;
    g_typeNesInfo.mapperType    |=  g_typeNesInfo.headers[7] & 0xF0;

    g_typeNesInfo.mirrorType     =  g_typeNesInfo.headers[6] & 0x01;
    g_typeNesInfo.backedRAMExist = (g_typeNesInfo.headers[6] & 0x02) ? 1 : 0;
    g_typeNesInfo.trainerExist   = (g_typeNesInfo.headers[6] & 0x04) ? 1 : 0;
    g_typeNesInfo.VRAMExist      = (g_typeNesInfo.headers[6] & 0x08) ? 1 : 0;

    g_typeNesInfo.VSSystemExist  =  g_typeNesInfo.headers[7] & 0x01;
    g_typeNesInfo.TVSystemMode   =  g_typeNesInfo.headers[9] & 0x01;

    printf("NES information:\r\n");
    printf("PRG ROM size: %dByte, PRG RAM size: %dByte, CHR ROM size: %dByte\r\n",\
        g_typeNesInfo.PRG_ROMSize,\
        g_typeNesInfo.PRG_RAMSize,\
        g_typeNesInfo.CHR_ROMSize);
    printf("mapper: %d, mirrorType: %d, backedRAMExist: %d, trainerExist: %d, VRAMExist: %d\r\n",\
        g_typeNesInfo.mapperType,\
        g_typeNesInfo.mirrorType,\
        g_typeNesInfo.backedRAMExist,\
        g_typeNesInfo.trainerExist,\
        g_typeNesInfo.VRAMExist);


    /* 设置mapper结构体 */
    vNesMapperTypeSet(g_typeNesInfo.mapperType);


    /* 代码区域起始位置指针 */
    g_typeNesInfo.PRG_ROM = &g_typeNesInfo.NES_ROM[16];
    /* 图案区域起始位置指针 */
    g_typeNesInfo.CHR_ROM = &g_typeNesInfo.NES_ROM[16 + g_typeNesInfo.PRG_ROMSize];


    /* 加载程序代码到CPU */
    vNesCPUMemoryLoad(0, 0, 1024 * 16);

    /* 加载图案信息到PPU */
    vNesPPUMemoryLoad(0, 0, 1024 * 8);

    return 0;
}

int8_t cNesStart(void)
{
    vNesPPUStart();
    vNesCPUStart();

    /* 记录当前系统时间 */
    g_typeNesInfo.timeStart = lNesTimeGet();

    g_typeNesInfo.runing = 1;

    return 0;
}

int8_t cNesStop(void)
{
    g_typeNesInfo.runing = 0;

    return 0;
}

int8_t cNesReset(void)
{
    cNesStop();

    return 0;
}

void vNesInit(void)
{
    memset(&g_typeNesInfo, 0, sizeof(g_typeNesInfo));

    g_typeNesInfo.ptypeCPUInfo = ptyeNesCPUInfoGet();
    g_typeNesInfo.ptypePPUInfo = ptyeNesPPUInfoGet();
    g_typeNesInfo.ptypeNesInputInfo = ptyeNesInputInfoGet();
    g_typeNesInfo.NES_ROM = nesRom;


    vNesPPUInit();
    vNesCPUInit();


    cNesFlashLoad("./NES/ROM/[218]  角色类 - 魂斗罗1.nes");

    /* 加载固件到模拟器 */
    cNesLoad();


    cNesStart();
}

void vNesExec(void)
{
    vNesCPUExec();

    vNesPPUExec();
}

NesInfoType *ptyeNesInfoGet(void)
{
    return &g_typeNesInfo;
}
